home *** CD-ROM | disk | FTP | other *** search
- /* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
- This file is part of the GNU C Library.
-
- The GNU C Library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
-
- The GNU C Library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with the GNU C Library; see the file COPYING.LIB. If
- not, write to the Free Software Foundation, Inc., 675 Mass Ave,
- Cambridge, MA 02139, USA. */
-
- #include <ansidecl.h>
- #include <errno.h>
- #include <stddef.h>
- #include <signal.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <unistd.h>
- #include <fcntl.h>
-
- #define SH_PATH "/bin/sh" /* Shell to run. */
- #define SH_NAME "sh" /* Name to give it. */
-
- /* Structure describing a popen child. */
- struct child
- {
- pid_t pid; /* PID of the child. */
- __ptr_t cookie; /* Original cookie from fdopen. */
- __io_functions funcs; /* Original functions from fdopen. */
- };
-
- /* io_functions for pipe streams.
- These all simply call the corresponding
- original function with the original cookie. */
-
- #define FUNC(type, name, args) \
- static type DEFUN(__CONCAT(child_,name), args, __CONCAT(name,decl)) \
- { \
- struct child *c = (struct child *) cookie; \
- { \
- __ptr_t cookie = c->cookie; \
- return (*c->funcs.__CONCAT(__,name)) args; \
- } \
- }
-
- #define readdecl PTR cookie AND register char *buf AND register size_t n
- FUNC (int, read, (cookie, buf, n))
- #define writedecl PTR cookie AND register CONST char *buf AND register size_t n
- FUNC (int, write, (cookie, buf, n))
- #define seekdecl PTR cookie AND fpos_t *pos AND int whence
- FUNC (int, seek, (cookie, pos, whence))
- #define closedecl PTR cookie
- FUNC (int, close, (cookie))
- #define filenodecl PTR cookie
- FUNC (int, fileno, (cookie))
-
- static const __io_functions child_funcs
- = { child_read, child_write, child_seek, child_close, child_fileno };
-
- /* Open a new stream that is a one-way pipe to a
- child process running the given shell command. */
- FILE *
- DEFUN(popen, (command, mode), CONST char *command AND CONST char *mode)
- {
- pid_t pid;
- int pipedes[2];
- FILE *stream;
- struct child *child;
-
- if (command == NULL || mode == NULL || (*mode != 'r' && *mode != 'w'))
- {
- errno = EINVAL;
- return NULL;
- }
-
- /* Create the pipe. */
- if (pipe(pipedes) < 0)
- return NULL;
-
- /* Fork off the child. */
- pid = __vfork ();
- if (pid == (pid_t) -1)
- {
- /* The fork failed. */
- (void) close (pipedes[0]);
- (void) close (pipedes[1]);
- return NULL;
- }
- else if (pid == (pid_t) 0)
- {
- /* We are the child side. Make the write side of
- the pipe be stdin or the read side be stdout. */
-
- CONST char *new_argv[4];
-
- if ((*mode == 'w' ? dup2(pipedes[STDIN_FILENO], STDIN_FILENO) :
- dup2(pipedes[STDOUT_FILENO], STDOUT_FILENO)) < 0)
- _exit(127);
-
- /* Close the pipe descriptors. */
- (void) close(pipedes[STDIN_FILENO]);
- (void) close(pipedes[STDOUT_FILENO]);
-
- /* Exec the shell. */
- new_argv[0] = SH_NAME;
- new_argv[1] = "-c";
- new_argv[2] = command;
- new_argv[3] = NULL;
- (void) execve(SH_PATH, (char *CONST *) new_argv, environ);
- /* Die if it failed. */
- _exit(127);
- }
-
- /* We are the parent side. */
-
- /* Close the irrelevant side of the pipe and open the relevant side as a
- new stream. Mark our side of the pipe to close on exec, so new children
- won't see it. */
- if (*mode == 'r')
- {
- (void) close (pipedes[STDOUT_FILENO]);
- (void) fcntl (pipedes[STDIN_FILENO], F_SETFD, FD_CLOEXEC);
- stream = fdopen (pipedes[STDIN_FILENO], mode);
- }
- else
- {
- (void) close (pipedes[STDIN_FILENO]);
- (void) fcntl (pipedes[STDOUT_FILENO], F_SETFD, FD_CLOEXEC);
- stream = fdopen (pipedes[STDOUT_FILENO], mode);
- }
-
- if (stream == NULL)
- goto error;
-
- child = (struct child *) malloc (sizeof (struct child));
- if (child == NULL)
- goto error;
-
- {
- /* Make sure STREAM has its functions set before
- we try to squirrel them away in CHILD. */
- extern void __stdio_check_funcs __P ((FILE *));
- __stdio_check_funcs (stream);
- }
-
- child->pid = pid;
- child->cookie = stream->__cookie;
- child->funcs = stream->__io_funcs;
- stream->__cookie = (PTR) child;
- stream->__io_funcs = child_funcs;
- stream->__ispipe = 1;
- return stream;
-
- error:
- {
- /* The stream couldn't be opened or the child structure couldn't be
- allocated. Kill the child and close the other side of the pipe. */
- int save = errno;
- (void) kill (pid, SIGKILL);
- if (stream == NULL)
- (void) close (pipedes[*mode == 'r' ? STDOUT_FILENO : STDIN_FILENO]);
- else
- (void) fclose (stream);
- #ifndef NO_WAITPID
- (void) waitpid (pid, (int *) NULL, 0);
- #else
- {
- pid_t dead;
- do
- dead = wait ((int *) NULL);
- while (dead > 0 && dead != pid);
- }
- #endif
- errno = save;
- return NULL;
- }
- }
-
- /* Close a stream opened by popen and return its status.
- Returns -1 if the stream was not opened by popen. */
- int
- DEFUN(pclose, (stream), register FILE *stream)
- {
- struct child *c;
- pid_t pid, dead;
- int status;
-
- if (!__validfp(stream) || !stream->__ispipe)
- {
- errno = EINVAL;
- return -1;
- }
-
- c = (struct child *) stream->__cookie;
- pid = c->pid;
- stream->__cookie = c->cookie;
- stream->__io_funcs = c->funcs;
- free ((PTR) c);
- stream->__ispipe = 0;
- if (fclose (stream))
- return -1;
-
- #ifndef NO_WAITPID
- dead = waitpid (pid, &status, 0);
- #else
- do
- dead = wait (&status);
- while (dead > 0 && dead != pid);
- #endif
- if (dead != pid)
- status = -1;
-
- return status;
- }
-